/**
 * @copyright Copyright (c) 2025 
 * CSG Digital Grid Technology (Guangdong) Co., Ltd.
 * @author xxxcsg
 */


#include "../3rd/mongoose.h"
#include "../3rd/cJSON.h"
#include "../common/PowerBindGather.h"

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <linux/if.h>
#include <arpa/inet.h>
#include <unistd.h> 
#include <string.h>

typedef char* caddr_t;

#define CONF_FILE "PowerBindTool.json"

//static const char *s_http_addr = "http://0.0.0.0:8000";   // HTTP port
//static const char *s_https_addr = "https://0.0.0.0:8443"; // HTTPS port
static const char *s_root_dir = ".";
int exit_now = 0;

static const char *s_json_header =
    "Content-Type: application/json\r\n"
    "Cache-Control: no-cache\r\n";

// Self signed certificates
// https://mongoose.ws/documentation/tutorials/tls/#self-signed-certificates
#ifdef TLS_TWOWAY
static const char *s_tls_ca =
    "-----BEGIN CERTIFICATE-----\n"
    "MIIBqjCCAU+gAwIBAgIUESoOPGqMhf9uarzblVFwzrQweMcwCgYIKoZIzj0EAwIw\n"
    "RDELMAkGA1UEBhMCSUUxDzANBgNVBAcMBkR1YmxpbjEQMA4GA1UECgwHQ2VzYW50\n"
    "YTESMBAGA1UEAwwJVGVzdCBSb290MCAXDTIwMDUwOTIxNTE0NFoYDzIwNTAwNTA5\n"
    "MjE1MTQ0WjBEMQswCQYDVQQGEwJJRTEPMA0GA1UEBwwGRHVibGluMRAwDgYDVQQK\n"
    "DAdDZXNhbnRhMRIwEAYDVQQDDAlUZXN0IFJvb3QwWTATBgcqhkjOPQIBBggqhkjO\n"
    "PQMBBwNCAAQsq9ECZiSW1xI+CVBP8VDuUehVA166sR2YsnJ5J6gbMQ1dUCH/QvLa\n"
    "dBdeU7JlQcH8hN5KEbmM9BnZxMor6ussox0wGzAMBgNVHRMEBTADAQH/MAsGA1Ud\n"
    "DwQEAwIBrjAKBggqhkjOPQQDAgNJADBGAiEAnHFsAIwGQQyRL81B04dH6d86Iq0l\n"
    "fL8OKzndegxOaB0CIQCPwSIwEGFdURDqCC0CY2dnMrUGY5ZXu3hHCojZGS7zvg==\n"
    "-----END CERTIFICATE-----\n";
#endif
static const char *s_tls_cert =
    "-----BEGIN CERTIFICATE-----\n"
    "MIIBhzCCASygAwIBAgIUbnMoVd8TtWH1T09dANkK2LU6IUswCgYIKoZIzj0EAwIw\n"
    "RDELMAkGA1UEBhMCSUUxDzANBgNVBAcMBkR1YmxpbjEQMA4GA1UECgwHQ2VzYW50\n"
    "YTESMBAGA1UEAwwJVGVzdCBSb290MB4XDTIwMDUwOTIxNTE0OVoXDTMwMDUwOTIx\n"
    "NTE0OVowETEPMA0GA1UEAwwGc2VydmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD\n"
    "QgAEkuBGnInDN6l06zVVQ1VcrOvH5FDu9MC6FwJc2e201P8hEpq0Q/SJS2nkbSuW\n"
    "H/wBTTBaeXN2uhlBzMUWK790KKMvMC0wCQYDVR0TBAIwADALBgNVHQ8EBAMCA6gw\n"
    "EwYDVR0lBAwwCgYIKwYBBQUHAwEwCgYIKoZIzj0EAwIDSQAwRgIhAPo6xx7LjCdZ\n"
    "QY133XvLjAgVFrlucOZHONFVQuDXZsjwAiEAzHBNligA08c5U3SySYcnkhurGg50\n"
    "BllCI0eYQ9ggp/o=\n"
    "-----END CERTIFICATE-----\n";

static const char *s_tls_key =
    "-----BEGIN PRIVATE KEY-----\n"
    "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQglNni0t9Dg9icgG8w\n"
    "kbfxWSS+TuNgbtNybIQXcm3NHpmhRANCAASS4EacicM3qXTrNVVDVVys68fkUO70\n"
    "wLoXAlzZ7bTU/yESmrRD9IlLaeRtK5Yf/AFNMFp5c3a6GUHMxRYrv3Qo\n"
    "-----END PRIVATE KEY-----\n";

unsigned long GetLocalIP()
{
	int MAXINTERFACES = 16;
	unsigned long ip = 0;
	int fd, intrface;
	struct ifreq buf[MAXINTERFACES];
	struct ifconf ifc;

	if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0)
	{
		ifc.ifc_len = sizeof(buf);
		ifc.ifc_buf = (caddr_t)buf;
		if (!ioctl(fd, SIOCGIFCONF, (char *)&ifc))
		{
			intrface = ifc.ifc_len / sizeof(struct ifreq);
			while (intrface-- > 0)
			{
				if (!(ioctl(fd, SIOCGIFADDR, (char *)&buf[intrface])))
				{
					ip = ((struct sockaddr_in*)(&buf[intrface].ifr_addr))->sin_addr.s_addr;
					break;
				}
			}
		}
		close(fd);
	}
	return ip;
}

/*
static void timer_fn(void *param)
{
    static int count = 0;
    printf("timer_fn %d\n", count++);
}
*/

static void handle_api_stats(struct mg_connection *c)
{
    // Print some statistics about currently established connections
    mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
    mg_http_printf_chunk(c, "ID PROTO TYPE      LOCAL           REMOTE\n");
    for (struct mg_connection *t = c->mgr->conns; t != NULL; t = t->next)
    {
        mg_http_printf_chunk(c, "%-3lu %4s %s %M %M\n", t->id,
                             t->is_udp ? "UDP" : "TCP",
                             t->is_listening  ? "LISTENING"
                             : t->is_accepted ? "ACCEPTED "
                                              : "CONNECTED",
                             mg_print_ip, &t->loc, mg_print_ip, &t->rem);
    }
    mg_http_printf_chunk(c, ""); // Don't forget the last empty chunk
}

static void handle_api_f2(struct mg_connection *c, struct mg_http_message *hm)
{
    mg_http_reply(c, 200, "", "{\"result\": \"%.*s\"}\n", (int)hm->uri.len, hm->uri.buf);
}

static void handle_bindinfologin(struct mg_connection *c)
{
    // const char *body_str = "{\"sites\":{\"site\":[{\"id\":\"1\",\"name\":\"菜鸟教程\",\"url\":\"www.runoob.com\"},{\"id\":\"2\",\"name\":\"菜鸟工具\",\"url\":\"www.jyshare.com\"},{\"id\":\"3\",\"name\":\"Google\",\"url\":\"www.google.com\"}]}}";   
    int ret = 0;
    char *body_str = NULL;
    ret = gather_login_info_json(&body_str);
    if (ret == 0)
    {
        mg_http_reply(c, 200, s_json_header, body_str);
        cJSON_free(body_str);
    }
}

static void handle_bindinfosinglecheck(struct mg_connection *c, struct mg_http_message *hm)
{
    // const char *body_str = "{\"sites\":{\"site\":[{\"id\":\"1\",\"name\":\"菜鸟教程\",\"url\":\"www.runoob.com\"},{\"id\":\"2\",\"name\":\"菜鸟工具\",\"url\":\"www.jyshare.com\"},{\"id\":\"3\",\"name\":\"Google\",\"url\":\"www.google.com\"}]}}";   
    int ret = 0;
    char *path_str = hm->body.buf;
    char *body_str = NULL;
    ret = gather_component_singlefile_info_json(path_str,&body_str);
    if (ret == 0)
    {
        mg_http_reply(c, 200, s_json_header, body_str);
        cJSON_free(body_str);
    }
}

static void handle_bindinfotools(struct mg_connection *c, struct mg_http_message *hm)
{
    // const char *body_str = "{\"sites\":{\"site\":[{\"id\":\"1\",\"name\":\"菜鸟教程\",\"url\":\"www.runoob.com\"},{\"id\":\"2\",\"name\":\"菜鸟工具\",\"url\":\"www.jyshare.com\"},{\"id\":\"3\",\"name\":\"Google\",\"url\":\"www.google.com\"}]}}";   
    int ret = 0;
    char *path_str = hm->body.buf;
    char *body_str = NULL;
    ret = gather_component_info_tools_json(path_str,&body_str);
    if (ret == 0)
    {
        mg_http_reply(c, 200, s_json_header, body_str);
        cJSON_free(body_str);
    }
}

static void handle_bindinfocheck(struct mg_connection *c)
{
    // const char *body_str = "{\"sites\":{\"site\":[{\"id\":\"1\",\"name\":\"菜鸟教程\",\"url\":\"www.runoob.com\"},{\"id\":\"2\",\"name\":\"菜鸟工具\",\"url\":\"www.jyshare.com\"},{\"id\":\"3\",\"name\":\"Google\",\"url\":\"www.google.com\"}]}}";   

    int ret = 0;
    char *body_str = NULL;
    ret = gather_component_info_json(CONF_FILE, &body_str);
    if (ret == 0)
    {
        mg_http_reply(c, 200, s_json_header, body_str);
        cJSON_free(body_str);
    }
}
static void udp_ev_handler(struct mg_connection *c, int ev, void *ev_data)
{
    if (ev == MG_EV_READ) 
    {
        uint8_t data[6];
        uint32_t localip = GetLocalIP();
        data[0] = 0x5A;
        data[1] = 0xA5;
		data[2] = (localip >> 0) & 0xff;
		data[3] = (localip >> 8) & 0xff;
		data[4] = (localip >> 16) & 0xff;
		data[5] = (localip >> 24) & 0xff; 

        mg_send(c, data, 6); // Send data
        c->recv.len = 0;     // Clean receive buffer
        //c->is_draining = 1;  // Close this connection when the response is sent
        printf("udp mg_send ip:%d.%d.%d.%d ......\n",data[2],data[3],data[4],data[5]);
    }
}

// We use the same event handler function for HTTP and HTTPS connections
// fn_data is NULL for plain HTTP, and non-NULL for HTTPS
static void ev_handler(struct mg_connection *c, int ev, void *ev_data)
{
    if (ev == MG_EV_ACCEPT && c->fn_data != NULL)
    {
        struct mg_tls_opts opts = {
#ifdef TLS_TWOWAY
            .ca = mg_str(s_tls_ca),
#endif
            .cert = mg_str(s_tls_cert),
            .key = mg_str(s_tls_key)};
        mg_tls_init(c, &opts);
    }
    if (ev == MG_EV_HTTP_MSG)
    {
        struct mg_http_message *hm = (struct mg_http_message *)ev_data;
        if (mg_match(hm->uri, mg_str("/api/stats"), NULL))
        {
            handle_api_stats(c);
        }
        else if (mg_match(hm->uri, mg_str("/api/f2/*"), NULL))
        {
            handle_api_f2(c, hm);
        }
        else if (mg_match(hm->uri, mg_str("/bindinfocheck"), NULL))
        {
            handle_bindinfocheck(c);
        }
        else if (mg_match(hm->uri, mg_str("/bindinfosinglecheck"), NULL))
        {
            handle_bindinfosinglecheck(c,hm);
        }
        else if (mg_match(hm->uri, mg_str("/bindinfotools"), NULL))
        {
            handle_bindinfotools(c,hm);
        }
        else if (mg_match(hm->uri, mg_str("/login"), NULL))
        {
            handle_bindinfologin(c);
        }
        else
        {
            struct mg_http_serve_opts opts;
            memset(&opts, '\0', sizeof(opts));
            opts.root_dir = s_root_dir;
            mg_http_serve_dir(c, ev_data, &opts);
        }
        MG_DEBUG(("%lu %.*s %.*s -> %.*s", c->id, (int)hm->method.len,
                  hm->method.buf, (int)hm->uri.len, hm->uri.buf, (int)3,
                  &c->send.buf[9]));
    }
}

void run_mongoose(int argc, char* argv[])
{
    printf("starting ......\n");
    struct mg_mgr mgr;       // Event manager
    char http_addr[32] = { 0 };
    char udp_addr[32] = { 0 };
    int port = 8080;
    mg_log_set(MG_LL_DEBUG); // Set log level
    mg_mgr_init(&mgr);       // Initialise event manager
    // mg_timer_add(&mgr, 1000, MG_TIMER_RUN_NOW | MG_TIMER_REPEAT, timer_fn, NULL);
    if(argc != 2)
    {
        port = 8080;
    }
    else
    {
        port = atoi(argv[1]);
    }
    snprintf(http_addr, 31, "http://0.0.0.0:%d", port);
    mg_http_listen(&mgr, http_addr, ev_handler, NULL);       // Create HTTP listener
    // mg_http_listen(&mgr, s_https_addr, ev_handler, (void *)1); // HTTPS listener

    snprintf(udp_addr, 31, "udp://0.0.0.0:%d", port+1);
    mg_listen(&mgr,udp_addr,udp_ev_handler,NULL);
    printf("started\n");
    while (!exit_now)
    {
        mg_mgr_poll(&mgr, 1000); // Infinite event loop
    }
    mg_mgr_free(&mgr);
}

int main(int argc, char* argv[])
{
    run_mongoose(argc, argv);
    return 0;
}
